package com.nineclock.auth.config;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.nineclock.auth.intergration.entity.IntergrationAuthenticationEntity;
import com.nineclock.auth.intergration.processor.AuthenticationProcessor;
import com.nineclock.auth.intergration.threadlocal.IntergrationAuthenticationHolder;
import com.nineclock.auth.intergration.threadlocal.UserHolder;
import com.nineclock.common.constant.NcConstant;
import com.nineclock.common.entity.NcAuthority;
import com.nineclock.common.entity.Result;
import com.nineclock.common.entity.UserInfo;
import com.nineclock.common.enums.ResponseEnum;
import com.nineclock.common.exception.NcException;
import com.nineclock.common.utils.BeanHelper;
import com.nineclock.system.dto.SysCompanyUserDTO;
import com.nineclock.system.dto.SysRoleDTO;
import com.nineclock.system.dto.SysUserDTO;
import com.nineclock.system.feign.SysUserFeign;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.Optional;

/**
 * 自定义用户详情类
 */

@Component
public class UserDetailsServiceImpl implements UserDetailsService {

    //两个认证处理器都实现了 AuthenticationProcessor,直接注入一个集合
    @Autowired
    private List<AuthenticationProcessor> processorList;

    @Autowired
    private SysUserFeign sysUserFeign;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

        //1.从ThreadLocal中获取参数
        IntergrationAuthenticationEntity entity = IntergrationAuthenticationHolder.get();

        //2.获取authType
        String authType = entity.getAuthType();

        //3.根据authType 选择认证处理器(自定义choose方法)
        AuthenticationProcessor authenticationProcessor = this.chooseAuthenticationProcessor(entity);

        //4.校验
        if (authenticationProcessor == null) {
            throw new NcException(ResponseEnum.SERVER_ERROR);
        }

        //5.调用认证处理器的认证方法
        SysUserDTO userDTO = authenticationProcessor.authenticate(entity);

        //6.组装数据返回  类型:UserDetails --> 实现类 User(username,password,权限列表)
        //User loginUser = new User(username,userDTO.getPassword(),AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_ADMIN"));

        //功能完善---JWT令牌包含用户信息,将username-->userDto转Json
        //但是userDto实体类包含password,所以使用新定义的UserInfo实体
        UserInfo userInfo = BeanHelper.copyProperties(userDTO, UserInfo.class);

        //todo -------------------- 认证功能完善
        //判断是否为 PC端认证
        String clientId = entity.getAuthParameter("client_id");
        String companyId = entity.getAuthParameter("company_id");

        //如果是PC
        if (NcConstant.PC_CLIENT.equals(clientId)) {

            //根据userId查询该用户对应的企业员工数据
            Result<List<SysCompanyUserDTO>> result = sysUserFeign.queryCompanyUser(userDTO.getId(), null);
            List<SysCompanyUserDTO> companyUserDTOList = result.getData();

            if (CollectionUtil.isEmpty(companyUserDTOList)) { //没有对应员工
                throw new NcException(ResponseEnum.USER_NOT_JOIN_COMPANY);
            }
            //判定用户是否是管理员(ROLE_ADMIN)
            for (SysCompanyUserDTO companyUserDTO : companyUserDTOList) {

                Boolean isAdmin = this.isAdmin(companyUserDTO);
                if (!isAdmin) { //不是企业管理员
                    throw new NcException(ResponseEnum.USER_NOT_COMPANY_ADMIN);
                }
            }

            //判定用户关联的企业, 如果只有一个, 封装企业信息 权限信息
            if (companyUserDTOList.size() == 1) {
                wrapperUserInfo(userInfo, companyUserDTOList.get(0));
            }
            //如果多个,选择了企业(传递进companyId)
            if (StrUtil.isNotEmpty(companyId)) {

                Result<List<SysCompanyUserDTO>> listResult = sysUserFeign.queryCompanyUser(userDTO.getId(), Long.valueOf(companyId));
                List<SysCompanyUserDTO> userDTOS = listResult.getData();
                if (CollectionUtil.isNotEmpty(userDTOS)) {
                    wrapperUserInfo(userInfo,userDTOS.get(0));
                }
            }

        //APP端
        } else {
            //判定是否传递companyId,如果为传递,获取最后一次登陆的企业
            if(StrUtil.isEmpty(companyId)){
                companyId = String.valueOf(userDTO.getLastLoginCompanyId());
            }

            //根据userId companyId查询关联的员工信息
            Result<List<SysCompanyUserDTO>> result = sysUserFeign.queryCompanyUser(userDTO.getId(), Long.valueOf(companyId));
            List<SysCompanyUserDTO> userDTOList = result.getData();

            //数据封装
            if(CollectionUtil.isNotEmpty(userDTOList)){
                wrapperUserInfo(userInfo,userDTOList.get(0));
            }
        }



        //为了封装统一响应结果,把userInfo 存入ThreadLocal,在aop后置增强 取出,封装到返回结果中
        UserHolder.set(userInfo);

        //6.组装数据并返回
        User loginUser = new User(JSON.toJSONString(userInfo), userDTO.getPassword(), userInfo.getGrantedAuthorities());

        return loginUser;
    }

    /**
     * 根据authType选择认证处理器
     */
    private AuthenticationProcessor chooseAuthenticationProcessor(IntergrationAuthenticationEntity entity) {
        //通过stream流+lambda 遍历集合,传入entity,从中选择一个认证处理器
        //Optional 1.8之后的对象,避免空指针
        Optional<AuthenticationProcessor> optional =
                processorList.stream()
                        .filter(authenticationProcessor -> authenticationProcessor.support(entity)).findFirst();

        if (optional.isPresent()) {
            return optional.get();
        }
        return null;
    }

    /**
     * 判定用户是否是企业管理员
     *
     * @param companyUserDTO
     * @return
     */
    private Boolean isAdmin(SysCompanyUserDTO companyUserDTO) {
        Boolean isAdmin = false;
        List<SysRoleDTO> roles = companyUserDTO.getRoles();
        if (CollectionUtil.isNotEmpty(roles)) {
            for (SysRoleDTO role : roles) {
                if (role.getRoleName().startsWith(NcConstant.ADMIN_ROLE_PREFIX)) {
                    isAdmin = true;
                }
            }
        }
        return isAdmin;
    }

    /**
     * 组装用户的基本信息 , 关联的企业信息
     *
     * @param userInfo
     * @param sysCompanyUserDTO
     */
    private void wrapperUserInfo(UserInfo userInfo, SysCompanyUserDTO sysCompanyUserDTO) {
        userInfo.setCompanyId(sysCompanyUserDTO.getCompanyId());
        userInfo.setCompanyName(sysCompanyUserDTO.getCompanyName());
        userInfo.setDepartmentId(sysCompanyUserDTO.getDepartmentId());
        userInfo.setDepartmentName(sysCompanyUserDTO.getDepartmentName());
        userInfo.setCompanyUserId(sysCompanyUserDTO.getId());
        userInfo.setPost(sysCompanyUserDTO.getPost());
        userInfo.setWorkNumber(sysCompanyUserDTO.getWorkNumber());
        userInfo.setTimeEntry(sysCompanyUserDTO.getTimeEntry());
        userInfo.setEnable(true);

        //用户的权限
        List<NcAuthority> grantedAuthorities = sysCompanyUserDTO.getGrantedAuthorities();

        if (!CollectionUtil.isEmpty(grantedAuthorities)) {
            userInfo.setGrantedAuthorities(grantedAuthorities);
        }
    }

}